﻿#include <iostream>
#include "config.h"
#ifdef _DEBUG
# pragma comment(lib, "..\\..\\lib\\Debug\\Utils-d.lib")
# pragma comment(lib, "..\\..\\lib\\Debug\\Elements-d.lib")
#else
# pragma comment(lib, "..\\..\\lib\\Release\\Utils.lib")
# pragma comment(lib, "..\\..\\lib\\Release\\Elements.lib")
#endif // _DEBUG

#include "Utils\\AWindow.h"
#include "Utils\\ScreenManager.h"
#include "Utils\\IScreen.h"
#include "Utils\\IScreen.h"
#include "Elements\\PushButton.h"
#include "Elements\\CheckBox.h"
#include "Elements\\Image.h"
#include <string>
#include <vector>
#include <sstream>
#include <SDL/SDL_image.h>
#include <time.h>

using namespace SDL::UTILS;
using namespace SDL::ELEMENTS;

class HelpScreen : public IScreen
{
private:
	Image m_background;
	std::vector<Label*> m_text;

public:
	HelpScreen(Font& _font) : IScreen(), m_background("")
	{
		SDL_Surface* s = SDL_CreateRGBSurface(SDL_HWSURFACE, 600, 500, 32, 0, 0, 0, 0);
		SDL_FillRect(s, NULL, Color(128, 0, 128).ToUint32());
		m_background.surface->SetSurface(s);
		m_background.SetPosition(100, 50);

		int offsetx = 4, offsety = 12, count = 0;

		Label* l = new Label(&_font, Color());
		l->Render(L"1 - Police d'écriture:");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count++;
		l = new Label(&_font, Color());
		l->Render(L"Tu peux changer la police en premier paramètre du programme.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count++;
		l = new Label(&_font, Color());
		l->Render(L"(Si tu sais pas faire, demandes moi)");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;
		l = new Label(&_font, Color());
		l->Render(std::string("Police actuelle: ") + std::string(TTF_FontFaceFamilyName(_font.GetFont())));
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		
		count += 2;
		l = new Label(&_font, Color());
		l->Render(L"2 - Changer la taille de la police:");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 1;
		l = new Label(&_font, Color());
		l->Render(L"Tu peux donner la taille de la police en second paramètre du programme.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 1;
		l = new Label(&_font, Color());
		l->Render(L"(Si tu sais pas faire, demandes moi)");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;
		l = new Label(&_font, Color());
		std::ostringstream oss;
		oss << _font.GetSize();
		l->Render(std::string("Taille actuelle: ") + oss.str());
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;

		l = new Label(&_font, Color());
		l->Render(L"3 - Les boutons:");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 1;
		l = new Label(&_font, Color());
		l->Render(L"Il y a 3 \"PushButtons\" activables.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 1;
		l = new Label(&_font, Color());
		l->Render(L"Les autres sont la pour te montrer les différents états.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;
		l = new Label(&_font, Color());
		l->Render(L"Il y a une \"CheckBox\" et ses différents états.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;
		l = new Label(&_font, Color());
		l->Render(L"Pour changer les boutons, change les fichiers '.png'.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;

		l = new Label(&_font, Color());
		l->Render(L"N.B: Tu auras sûrement des bugs d'affichage pour le texte");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 1;
		l = new Label(&_font, Color());
		l->Render(L"(ligne pas finie si police trop grosse, ...), c'est normal");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 1;
		l = new Label(&_font, Color());
		l->Render(L"donc ne t'en soucies pas, occupes toi juste des boutons.");
		m_text.push_back(l);
		l->SetPosition(offsetx, offsety + _font.GetSize() * count);
		count += 2;

		l = new Label(&_font, Color());
		l->Render(L"Appuies sur 'H' pour fermer l'aide.");
		m_text.push_back(l);
		l->SetPosition((m_background.surface->GetW() / 2) - (l->surface->GetW() / 2), offsety + _font.GetSize() * count);
	}

	virtual ~HelpScreen()
	{
		for (int i = 0; i < m_text.size(); i++)
			delete m_text[i];
		m_text.clear();
	}

	virtual void Draw(IArgs* _args)
	{
		ScreenArgs* sa = (ScreenArgs*)_args;
		m_background.Draw(sa->Screen);
		for (int i = 0; i < m_text.size(); i++)
			m_text[i]->Draw(m_background.surface->surface);
	}

	virtual void Exec(IArgs* _args)
	{
	}

	static bool Func(ScreenManager::Element* _caller, IArgs* _datas)
	{
		ScreenArgs* sa = (ScreenArgs*)_caller->Datas;
		if (sa->Hide == false)
			_caller->Object->Draw(_caller->Datas);
		return (true);
	}
};

struct Hakata
{
public:
	static Surface Avatar;
	Rect  Position;
	float Speed;
	float DirectionX;
	float DirectionY;

public:
	Hakata() : Speed(2.0), DirectionX(1.2), DirectionY(1.3)
	{
		static bool init = false;
		if (init == false)
		{
			srand(time(NULL));
			init = true;
		}
		Position.x = (rand() % ((800 - Avatar.GetW()) - 0 + 1) + 0);
		Position.y = (rand() % ((600 - Avatar.GetH()) - 0 + 1) + 0);
	}

	~Hakata() {}

	void Move()
	{
		Position.x += (Sint16)(DirectionX * Speed);
		Position.y += (Sint16)(DirectionY * Speed);
		if (Position.x <= 0 || Position.x + Avatar.GetW() >= 800)
		{
			DirectionX *= -1;
			if (Position.x < 0)
				Position.x = 0;
			else if (Position.x + Avatar.GetW() > 800)
				Position.x = 800 - Avatar.GetW();
		}
		if (Position.y <= 0 || Position.y + Avatar.GetH() >= 600)
		{
			DirectionY *= -1;
			if (Position.y < 0)
				Position.y = 0;
			else if (Position.y + Avatar.GetH() > 600)
				Position.y = 600 - Avatar.GetH();
		}
	}

	void Draw(SDL_Surface* _screen)
	{
		Avatar.BlitSurface(NULL, _screen, &Position);
	}
};

Surface Hakata::Avatar = Surface(IMG_Load("Hakata_resized.png"));

class MainScreen : public IScreen
{
private:
	Font& m_font;
	PushButton m_pbTiny;
	PushButton m_pbNormal;
	PushButton m_pbBig;
	CheckBox m_cb;

	PushButton m_pbNormalReleased;
	PushButton m_pbNormalHovered;
	PushButton m_pbNormalPressed;
	PushButton m_pbNormalClicked;

	CheckBox m_cbReleased;
	CheckBox m_cbHovered;
	CheckBox m_cbPressed;
	CheckBox m_cbClicked;
	Label m_helpText;
	std::vector<Hakata> m_hakatas;

public:
	MainScreen(Font& _font) : IScreen(), m_font(_font), m_pbTiny(L"A", Rect()), m_pbNormal(L"Normal"), m_pbBig(L"A", Rect(0, 0, 300, 300)), m_cb(L"My beautiful checkbox"),
		m_pbNormalReleased(L"Released"),m_pbNormalHovered(L"Hovered"), m_pbNormalPressed(L"Pressed"), m_pbNormalClicked(L"Clicked"),
		m_cbReleased(L"My beautiful checkbox released"), m_cbHovered(L"My beautiful checkbox hovered"), m_cbPressed(L"My beautiful checkbox Pressed"),
		m_cbClicked(L"My beautiful checkbox clicked"), m_helpText(&m_font, Color((Uint8)255)), m_hakatas()
	{
		m_pbTiny.SetFont(&m_font);
		m_pbNormal.SetFont(&m_font);
		m_pbBig.SetFont(&m_font);
		m_cb.SetFont(&m_font);

		m_pbNormalReleased.SetFont(&m_font);
		m_pbNormalHovered.SetFont(&m_font);
		m_pbNormalPressed.SetFont(&m_font);
		m_pbNormalClicked.SetFont(&m_font);

		m_cbReleased.SetFont(&m_font);
		m_cbHovered.SetFont(&m_font);
		m_cbPressed.SetFont(&m_font);
		m_cbClicked.SetFont(&m_font);

		m_pbTiny.SetPosition(100, 42);
		m_pbNormal.SetPosition(200, 42);
		m_pbBig.SetPosition(400, 42);
		m_cb.SetPosition(100, 500);
		
		m_pbNormalReleased.SetPosition(200, 126);
		m_pbNormalReleased.state = SDL::BUTTON::STATE::RELEASED;
		m_pbNormalHovered.SetPosition(200, 168);
		m_pbNormalHovered.state = SDL::BUTTON::STATE::HOVERED;
		m_pbNormalPressed.SetPosition(200, 210);
		m_pbNormalPressed.state = SDL::BUTTON::STATE::PRESSED;
		m_pbNormalClicked.SetPosition(200, 252);
		m_pbNormalClicked.state = SDL::BUTTON::STATE::CLICKED;
		
		m_cbReleased.SetPosition(400, 450);
		m_cbReleased.state = SDL::BUTTON::STATE::RELEASED;
		m_cbHovered.SetPosition(400, 492);
		m_cbHovered.state = SDL::BUTTON::STATE::HOVERED;
		m_cbPressed.SetPosition(400, 534);
		m_cbPressed.state = SDL::BUTTON::STATE::PRESSED;
		m_cbClicked.SetPosition(400, 572);
		m_cbClicked.state = SDL::BUTTON::STATE::CLICKED;

		m_helpText.Render("Appuies sur 'h' pour afficher ou masquer l'aide", Font::RenderShaded);
		m_helpText.SetPosition(400 - m_helpText.surface->GetW() / 2, 375);
	}
	
	virtual ~MainScreen()
	{
	}

	virtual void Draw(IArgs* _datas)
	{
		ScreenArgs* sa = (ScreenArgs*)_datas;

		for (int i = 0; i < m_hakatas.size(); i++)
			m_hakatas[i].Draw(sa->Screen);

		m_pbTiny.Draw(sa->Screen);
		m_pbNormal.Draw(sa->Screen);
		m_pbBig.Draw(sa->Screen);
		m_cb.Draw(sa->Screen);
		
		m_pbNormalReleased.Draw(sa->Screen);
		m_pbNormalHovered.Draw(sa->Screen);
		m_pbNormalPressed.Draw(sa->Screen);
		m_pbNormalClicked.Draw(sa->Screen);

		m_cbReleased.Draw(sa->Screen);
		m_cbHovered.Draw(sa->Screen);
		m_cbPressed.Draw(sa->Screen);
		m_cbClicked.Draw(sa->Screen);

		m_helpText.Draw(sa->Screen);
	}

	virtual void Exec(IArgs* _datas)
	{
		m_pbTiny.UpdateState();
		m_pbNormal.UpdateState();
		m_pbBig.UpdateState();
		m_cb.UpdateState();

		m_cbReleased.Check(m_cb.IsChecked());
		m_cbHovered.Check(m_cb.IsChecked());
		m_cbClicked.Check(m_cb.IsChecked());
		m_cbPressed.Check(m_cb.IsChecked());

		SDL_Event* e = ((ScreenArgs*)_datas)->Event;
		if (e->type == SDL_KEYDOWN)
		{
			switch (e->key.keysym.sym)
			{
			case SDLK_a:
				m_hakatas.push_back(Hakata());
				//e->type = SDL_KEYUP;
				//SDL_PushEvent(e);
				std::cout << "Une Hakata a rejoint ce monde." << std::endl;
				std::cout << "Population: " << m_hakatas.size() << std::endl;
				break ;

			case SDLK_s:
				m_hakatas.pop_back();
				//e->type = SDL_KEYUP;
				//SDL_PushEvent(e);
				if (m_hakatas.size() > 0)
				{
					std::cout << "Une Hakata a quittee ce monde." << std::endl;
					std::cout << "Population: " << m_hakatas.size() << std::endl;
				}
				else
					std::cout << "Aucune Hakata trouvee." << std::endl;
				break ;
			}
		}
	}

	static bool Func(ScreenManager::Element* _caller, IArgs* _args)
	{
		ScreenArgs* sa = (ScreenArgs*)_caller->Datas;
		if (sa->Pause == false)
		{
			for (int i = 0; i < ((MainScreen*)_caller->Object)->m_hakatas.size(); i++)
				((MainScreen*)_caller->Object)->m_hakatas[i].Move();

			Uint8* keystate = SDL_GetKeyState(NULL);
			if (keystate[SDLK_PLUS] || keystate[SDLK_KP_PLUS])
			{
				for (int i = 0; i < ((MainScreen*)_caller->Object)->m_hakatas.size(); i++)
					((MainScreen*)_caller->Object)->m_hakatas[i].Speed += 0.3;
			}
			else if (keystate[SDLK_MINUS] || keystate[SDLK_KP_MINUS])
			{
				for (int i = 0; i < ((MainScreen*)_caller->Object)->m_hakatas.size(); i++)
				{
					if (((MainScreen*)_caller->Object)->m_hakatas[i].Speed - 0.3 >= 0.0)
						((MainScreen*)_caller->Object)->m_hakatas[i].Speed -= 0.3;
				}
			}
		}
		return (true);
	}
};

class Window : public AWindow
{
private:
	SDL_Event m_event;
	bool m_quit;
	ScreenManager m_screens;
	std::string m_currentScreen;
	Font* m_font;

public:
	Window() : AWindow(), m_quit(false)
	{
	}

	virtual ~Window()
	{
	}

	virtual bool Init(SDLConfig const& _config, std::string const& _path, int _size)
	{
		if (this->AWindow::Init(_config) == false)
			return (false);
		m_font = new Font(_path, _size);
		m_screens.Add("Main", new MainScreen(*m_font), MainScreen::Func,
					  new ScreenArgs(m_screen, &m_event));
		ScreenArgs* sa = new ScreenArgs(m_screen, &m_event);
		sa->Hide = true;
		m_screens.Add("Help", new HelpScreen(*m_font), HelpScreen::Func, sa);
		m_currentScreen = "Main";
		return (true);
	}

	virtual int Exec()
	{
		int tempsPrecedent = 0, tempsActuel = 0, refresh = 10;
		Color background = Color(64, 128, 128);

		while (!m_quit)
		{
			tempsActuel = SDL_GetTicks();
			if (tempsActuel - tempsPrecedent > refresh)
			{
				SDL_FillRect(m_screen, NULL, background.ToUint32());

				while (SDL_PollEvent(&m_event))
				{
					this->ManageEvents();
					m_screens.Exec();
				}
				m_screens.Manage();
				m_screens.Draw();

				SDL_Flip(m_screen);
				tempsPrecedent = tempsActuel;
			}
			else
				SDL_Delay(refresh - (tempsActuel - tempsPrecedent));
		}
		return (EXIT_SUCCESS);
	}

private:
	void ManageEvents()
	{
		switch (m_event.type)
		{
		case SDL_QUIT:
			m_quit = true;
			break ;

		case SDL_KEYDOWN:
			switch (m_event.key.keysym.sym)
			{
			case SDLK_ESCAPE:
				m_quit = true;
				break ;
				
			case SDLK_h:
				ScreenManager::Element* help = m_screens.GetElement("Help");
				ScreenManager::Element* main = m_screens.GetElement("Main");
				bool& hide = ((ScreenArgs*)(help->Datas))->Hide;
				bool& pause = ((ScreenArgs*)(main->Datas))->Pause;
				if (hide == false)
				{
					hide = true;
					pause = false;
				}
				else
				{
					hide = false;
					pause = true;
				}
				break ;
			}
			break ;
		}
	}
};

#include <fstream>

int main(int _argc, char** _argv)
{
	int size = 16;
	std::string path = "arial.ttf";

	if (_argc > 1)
	{
		path = _argv[1];
		if (_argc > 2)
		{
			std::istringstream iss(_argv[2]);
			iss >> size;
			if (size <= 0)
				size = 16;
		}
	}

	{
		std::ifstream f(path);
		if (f.fail())
		{
			std::cerr << "Erreur: Le fichier \"" << path << "\" est introuvable." << std::endl;
			std::cerr << "Usage: " << _argv[0] << " [font_path [font_size]]" << std::endl;
			std::cerr << "Appuiyez sur une \"entree\" pour continuer." << std::endl;
			getchar();
			return (EXIT_FAILURE);
		}
	}


	Window w;
	w.Init(SDLConfig(), path, size);
	return (w.Exec());
}
